﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;

using Infrastructure.Core;
using SMG.Domain.Repository;
using SMG.Domain.UnitOfWork;
using SMG.Domain.Specifications;
using Infrastructure.Core.Common;


namespace Infrastructure.Repository.EntityFramework
{
    /// <summary>
    /// ef仓储基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class EntityFrameworkRepository<T> : Repository<T>
        where T : class, IAggregateRoot
    {
        protected DbContext EFContext;

        public EntityFrameworkRepository(IRepositoryContext context)
            : base(context)
        {
            if (context is IEntityFrameWorkRepositoryContext)
                EFContext = ((IEntityFrameWorkRepositoryContext)context).EFContext;
            else throw new Exception("");
        }

        #region Override

        /// <summary>
        /// 根据主键查询聚合根
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public override T GetByKey(long id)
        {
            return EFContext.Set<T>().FirstOrDefault(t => t.ID == id);
        }
        /// <summary>
        /// 判断制定规约条件的聚合根是否存在
        /// </summary>
        /// <param name="spcification"></param>
        /// <returns></returns>
        public override bool Exist(ISpecification<T> specification)
        {
            Int32 count = EFContext.Set<T>().Count(specification.GetExpression());
            return count > 0;
        }
        /// <summary>
        /// 根据指定规约条件、排序方式查询聚合根
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="sortPredicate"></param>
        /// <param name="sortOrder"></param>
        /// <returns></returns>
        public override IQueryable<T> QueryBy(ISpecification<T> specification, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder)
        {
            var query = this.EFContext.Set<T>().Where(specification.GetExpression());
            switch (sortOrder) { 
                case SortOrder.Ascending:
                    query = query.SortBy<T>(sortPredicate);
                    break;
                case SortOrder.Descending:
                    query = query.SortByDescending<T>(sortPredicate);
                    break;
            }
            return query.AsNoTracking();
        }
        /// <summary>
        /// 根据指定规约条件、排序方式分页查询聚合根
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="sortPredicate"></param>
        /// <param name="sortOrder"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public override PageResult<T> QueryByPage(ISpecification<T> specification, Expression<Func<T, dynamic>> sortPredicate, SortOrder sortOrder, int pageSize, int pageIndex)
        {
            var skip = (pageIndex - 1) * pageSize;
            var take = pageSize;

            var query = this.EFContext.Set<T>().Where(specification.GetExpression());
            var count = query.Count();
            switch (sortOrder)
            {
                case SortOrder.Ascending:
                    query = query.SortBy<T>(sortPredicate);
                    break;
                case SortOrder.Descending:
                    query = query.SortByDescending<T>(sortPredicate);
                    break;
            }
            query = query.OrderBy(t => t.ID).AsNoTracking();
            return new PageResult<T>(pageSize, pageIndex, count, query.Skip(skip).Take(take).ToList());
        }

        #endregion

        public override void SetContext(IRepositoryContext context)
        {
            if (!(context is IEntityFrameWorkRepositoryContext))
                throw new Exception("");

            this.EFContext = ((IEntityFrameWorkRepositoryContext)context).EFContext;
            base.SetContext(context);
        }
        
    }
}
